﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;

using System.Drawing.Imaging;
namespace LowLevelGraphics.Filter
{
    /// <summary>
    /// 
    /// </summary>
    public class EdgeDetectHomogenity : BaseImageFilter
    {
        /// <summary>
        /// 
        /// </summary>
        protected byte m_nThreshold = 10;

        /// <summary>
        /// Initializes a new instance of the <see cref="EdgeDetectHomogenity"/> class.
        /// </summary>
        public EdgeDetectHomogenity()
            : this(10)
        {
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="EdgeDetectHomogenity"/> class.
        /// </summary>
        /// <param name="_nThreshold">The _n threshold.</param>
        public EdgeDetectHomogenity(byte _nThreshold)
        {
            m_nThreshold = _nThreshold;
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="EdgeDetectHomogenity"/> class.
        /// </summary>
        /// <param name="_edgeDetectHomogenity">The _edge detect homogenity.</param>
        internal EdgeDetectHomogenity(EdgeDetectHomogenity _edgeDetectHomogenity)
        {
            m_nThreshold = _edgeDetectHomogenity.m_nThreshold;
        }

        /// <summary>
        /// Gets or sets the threshold.
        /// </summary>
        /// <value>The threshold.</value>
        public byte Threshold
        {
            get { return m_nThreshold; }
            set { m_nThreshold = value; }
        }

        /// <summary>
        /// detect the homogenity Edges.
        /// </summary>
        /// <param name="_bitmap"></param>
        /// <returns></returns>
        public override UnsafeBitmap Execute(UnsafeBitmap _bitmap)
        {
			// This one works by working out the greatest difference between a pixel and it's eight neighbours.
			// The threshold allows softer edges to be forced down to black, use 0 to negate it's effect.
			UnsafeBitmap b2 = (UnsafeBitmap)_bitmap.Clone();

			// GDI+ still lies to us - the return format is BGR, NOT RGB.
			BitmapData bmData = _bitmap.BitmapData; //b.LockBits(new Rectangle(0, 0, b.Width, b.Height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);
            BitmapData bmData2 = b2.BitmapData; //b2.LockBits(new Rectangle(0, 0, b.Width, b.Height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);

			int stride = bmData.Stride;
			IntPtr Scan0 = bmData.Scan0;
			IntPtr Scan02 = bmData2.Scan0;

            unsafe
            {
                byte* p = (byte*)(void*)Scan0;
                byte* p2 = (byte*)(void*)Scan02;

                int nByteCount = _bitmap.BytesPerPixelCount;
                int nOffset = stride - _bitmap.Width * nByteCount; //stride - _bitmap.Width * 3
                int nWidth = _bitmap.Width * nByteCount; //_bitmap.Width * 3
                int nHeight = _bitmap.Height;
                int nPixel = 0, nPixelMax = 0;

                p += stride;
                p2 += stride;

                for (int y = 0; y < nHeight - nByteCount; ++y)
                {
                    p += nByteCount; //p += 3;
                    p2 += nByteCount; //p2 += 3;

                    for (int x = nByteCount; x < nWidth - nByteCount; ++x) //int x = 3; x < nWidth - 3; ++x
                    {
                        nPixelMax = Math.Abs(p2[0] - (p2 + stride - nByteCount)[0]);
                        nPixel = Math.Abs(p2[0] - (p2 + stride)[0]);
                        if (nPixel > nPixelMax) nPixelMax = nPixel;

                        nPixel = Math.Abs(p2[0] - (p2 + stride + nByteCount)[0]);
                        if (nPixel > nPixelMax) nPixelMax = nPixel;

                        nPixel = Math.Abs(p2[0] - (p2 - stride)[0]);
                        if (nPixel > nPixelMax) nPixelMax = nPixel;

                        nPixel = Math.Abs(p2[0] - (p2 + stride)[0]);
                        if (nPixel > nPixelMax) nPixelMax = nPixel;

                        nPixel = Math.Abs(p2[0] - (p2 - stride - nByteCount)[0]);
                        if (nPixel > nPixelMax) nPixelMax = nPixel;

                        nPixel = Math.Abs(p2[0] - (p2 - stride)[0]);
                        if (nPixel > nPixelMax) nPixelMax = nPixel;

                        nPixel = Math.Abs(p2[0] - (p2 - stride + nByteCount)[0]);
                        if (nPixel > nPixelMax) nPixelMax = nPixel;

                        if (nPixelMax < m_nThreshold) nPixelMax = 0;

                        p[0] = (byte)nPixelMax;

                        ++p;
                        ++p2;
                    }
                    p += nByteCount + nOffset; //p += 3 + nOffset;
                    p2 += nByteCount + nOffset; //p2 += 3 + nOffset;
                }
            }

            b2.Dispose(); // b2.UnlockBits(bmData2);
            return _bitmap;
        }

        /// <summary>
        /// This is used to clone a filter, each filter must implement it's own clone functionality
        /// </summary>
        /// <returns></returns>
        public override object Clone()
        {
            return new EdgeDetectHomogenity(this);
        }
    }
}
